package upm.pb4agent.team2;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;

import jade.content.AgentAction;
import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import upm.pb4agent.ontology.AssignBoat;
import upm.pb4agent.ontology.DenyRow;
import upm.pb4agent.ontology.EnteringDock;
import upm.pb4agent.ontology.PickSeat;
import upm.pb4agent.ontology.RaceFinished;
import upm.pb4agent.ontology.RaceStarted;
import upm.pb4agent.ontology.RealRow;
import upm.pb4agent.ontology.Row;
import upm.pb4agent.ontology.Seat;
import upm.pb4agent.ontology.SubscribeReferee;
import upm.pb4agent.team2.internalOntology.DestroyTeamPlease;
import upm.pb4agent.team2.internalOntology.HasRaceFinished;

public class Rower extends Team2BaseAgent 
{
	private static final long serialVersionUID = 2180642254916440177L;
	
	private AID myManagerAID;
	private AID myBoatAID;
	private AID watcherAID;
	
	private upm.pb4agent.ontology.Boat myOntologyBoat;
	
	private PickSeat currentPickSeat;
	
	private Long row_seconds;

	private boolean raceFinished = false;
	private boolean killMeSent = false;
	
	protected void setup() 
	{
		super.setup();
		
		try 
		{
			row_seconds = Long.valueOf(getPropertyFromConfigFile("row_seconds")) * 1000;
		}
		catch (NumberFormatException e) 
		{
			e.printStackTrace();
		} 
		catch (FileNotFoundException e) 
		{
			e.printStackTrace();
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		addBehaviour(new MessageListener(this));
		addBehaviour(new SubscribeToReferee(this));
		
		Object[] args = getArguments();
		if (args != null) 
		{
			myManagerAID = (AID) args[0];
		}
	}
	
	private String currentRowingStrategy = Constants.ROWING_STRATEGY_A;
	
	private void startRowing()
	{
			if (this.getLocalName().equals(Constants.ROWER21NAME))
			{
				if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_A) || currentRowingStrategy.equals(Constants.ROWING_STRATEGY_B))
				{
					sendRowRequest(Constants.OAR_3_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
					sendRowRequest(Constants.OAR_4_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
				else if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_C) || currentRowingStrategy.equals(Constants.ROWING_STRATEGY_D))
				{
					sendRowRequest(Constants.OAR_3_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
			}
			
			else if (this.getLocalName().equals(Constants.ROWER22NAME))
			{
				if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_B))
				{
					sendRowRequest(Constants.OAR_1_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
					sendRowRequest(Constants.OAR_2_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
				else if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_C) || currentRowingStrategy.equals(Constants.ROWING_STRATEGY_D))
				{
					sendRowRequest(Constants.OAR_4_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
			}
	
			else if (this.getLocalName().equals(Constants.ROWER23NAME))
			{
				if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_C))
				{
					sendRowRequest(Constants.OAR_1_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
					sendRowRequest(Constants.OAR_2_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
				if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_D))
				{
					sendRowRequest(Constants.OAR_1_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
			}
			
			else if (this.getLocalName().equals(Constants.ROWER24NAME))
			{
				if (currentRowingStrategy.equals(Constants.ROWING_STRATEGY_D))
				{
					sendRowRequest(Constants.OAR_2_ID, Constants.ROW_DIRECTION_FORWARD, Constants.ROW_STRENGTH_STRONG);
				}
			}
			
			askForUpdatesToManager();
	}
	
	private void sendRowRequest(String oarID, int direction, int strength)
	{
		Row row = new Row();
			row.setBoat(myOntologyBoat);
			row.setOarID(oarID);
			row.setDirection(direction);
			row.setStrength(strength);
		
		sendMessage(watcherAID, ACLMessage.REQUEST, new Action(watcherAID, row), Row.class.getSimpleName().toString());
	}

	private void askForUpdatesToManager()
	{
		//ask manager if RaceFinished has been informed by referee
		HasRaceFinished hasRaceFinished = new HasRaceFinished();
		sendInternalMessage(myManagerAID, ACLMessage.REQUEST, new Action(myManagerAID, hasRaceFinished), HasRaceFinished.class.getSimpleName().toString());
	}
	
	private void checkIfRaceFinishedOrSleep()
	{
		if (raceFinished)
		{
			if (!killMeSent)
			{
				// tell AgTeam2 To Destroy all team 2 agents
				AID agTeam2AID = findAgentOnDF(this, Constants.AGTEAM2);
				sendInternalMessage(agTeam2AID, ACLMessage.INFORM, new Action(agTeam2AID, new DestroyTeamPlease()), DestroyTeamPlease.class.getSimpleName().toString());
				killMeSent = true;
			}
		}
		else
		{
			try 
			{
				Thread.sleep(row_seconds / 2);
			} 
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
			
			startRowing();
		}
	}
	
	private PickSeat createPickSeat(int seatNumber)
	{
		Seat seat = new Seat();
		seat.setSeatID(Integer.toString(seatNumber));
		PickSeat pickSeat = new PickSeat();
		pickSeat.setSeatID(seat);
		
		return pickSeat;
	}
	
	private void setCurrentPickSeat(int seatNumber)
	{
		this.currentPickSeat = createPickSeat(seatNumber);
	}
	
	private void sendCurrentPickSeat(AID receiver)
	{
		sendMessage(receiver, ACLMessage.INFORM, new Action(receiver, this.currentPickSeat), PickSeat.class.getSimpleName().toString());
	}
	
	// -------------------------------------------
	// --------------- CLASSES -------------------
	// -------------------------------------------
	
	class SubscribeToReferee extends SimpleBehaviour 
	{

		private static final long serialVersionUID = -7143393353855383869L;
		private Boolean subscriptionSent = false;

		public SubscribeToReferee(Agent agent) 
		{
			super(agent);
		}

		@Override
		public void action() 
		{
			AID receiverAID = findAgentOnDF(myAgent, Constants.REFEREE);
			
			if (receiverAID != null)
			{
				sendSubscription(receiverAID);
			}
		}
		
		private void sendSubscription(AID receiverAID)
		{
			printOut("sending subscription request to referee...");
			
			SubscribeReferee subscribeReferee = new SubscribeReferee();
			Action action = new Action(receiverAID, subscribeReferee);
			
			sendMessage(receiverAID, ACLMessage.SUBSCRIBE, action, SubscribeReferee.class.getSimpleName().toString());
			
			subscriptionSent = true;
		}

		@Override
		public boolean done() 
		{
			return subscriptionSent;
		}
	}
	
	class MessageListener extends SimpleBehaviour 
	{
		private static final long serialVersionUID = 6257536137121967518L;

		public MessageListener(Agent agent) 
		{
			super(agent);
		}

		@Override
		public void action() 
		{
			ACLMessage aclMessage = receive(MessageTemplate.and(MessageTemplate.MatchLanguage(codec.getName()), MessageTemplate.or(MessageTemplate.MatchOntology(ontology.getName()), MessageTemplate.MatchOntology(internalOntology.getName()))));

			printReceivedProtocol(aclMessage);
			
			if (aclMessage != null) 
			{
				if (aclMessage.getPerformative() == ACLMessage.INFORM) 
				{
					try 
					{
						ContentElement contentElement = null;
						contentElement = getContentManager().extractContent(aclMessage);
						
						if (contentElement instanceof AgentAction)
						{
							Action action = (Action) contentElement;
							Concept concept = action.getAction();
							
							if (concept instanceof HasRaceFinished)
							{
								raceFinished = ((HasRaceFinished)concept).getHasFinished();
								checkIfRaceFinishedOrSleep();
							}

							//This, although would be useful, was not accepted by the rest of teams
							/*else if (concept instanceof PickSeat)
							{
								//PickSeat was allowed
								printOut("PickSeat request was accepted for seatID = "+((PickSeat)concept).getSeatID().getSeatID());
							}*/

							else if (concept instanceof RaceStarted)
							{
								if (myAgent.getLocalName().equals(Constants.ROWER21NAME))
								{
									startRowing();
								}
							}

							else if (concept instanceof EnteringDock)
							{
								printOut("EnteringDock "+((EnteringDock)concept).getDock().getName()+" at position "+((EnteringDock)concept).getPosition()+". Hurry up, let's win the race! :D");
								
								if (((EnteringDock)concept).getDock().getName().equals(Constants.DOCK_B_ID) || ((EnteringDock)concept).getDock().getName().equals(Constants.DOCK_B_ALTERNATE_ID))
								{
									currentRowingStrategy = Constants.ROWING_STRATEGY_B;
									if (myAgent.getLocalName().equals(Constants.ROWER22NAME))
									{
										setCurrentPickSeat(2);
										sendCurrentPickSeat(myBoatAID);
										startRowing();
									}
								}
								else if (((EnteringDock)concept).getDock().getName().equals(Constants.DOCK_C_ID) || ((EnteringDock)concept).getDock().getName().equals(Constants.DOCK_C_ALTERNATE_ID))
								{
									currentRowingStrategy = Constants.ROWING_STRATEGY_C;
									if (myAgent.getLocalName().equals(Constants.ROWER21NAME))
									{
										setCurrentPickSeat(4);
										sendCurrentPickSeat(myBoatAID);
									}
									else if (myAgent.getLocalName().equals(Constants.ROWER22NAME))
									{
										setCurrentPickSeat(6);
										sendCurrentPickSeat(myBoatAID);
									}
									else if (myAgent.getLocalName().equals(Constants.ROWER23NAME))
									{
										setCurrentPickSeat(2);
										sendCurrentPickSeat(myBoatAID);
										startRowing();
									}
								}
								else if (((EnteringDock)concept).getDock().getName().equals(Constants.DOCK_D_ID) || ((EnteringDock)concept).getDock().getName().equals(Constants.DOCK_D_ALTERNATE_ID))
								{
									currentRowingStrategy = Constants.ROWING_STRATEGY_D;
									if (myAgent.getLocalName().equals(Constants.ROWER23NAME))
									{
										setCurrentPickSeat(1);
										sendCurrentPickSeat(myBoatAID);
									}
									else if (myAgent.getLocalName().equals(Constants.ROWER24NAME))
									{
										setCurrentPickSeat(3);
										sendCurrentPickSeat(myBoatAID);
										startRowing();
									}
								}
							}

							else if (concept instanceof RaceFinished)
							{
								if(((RaceFinished)concept).getBoatAID().equals(myBoatAID))
								{
									printOut(" <<< !!!! FINISHED RACE IN POSITION # "+((RaceFinished)concept).getPosition() +" !!!! >>>");
								}
							}

							else if (concept instanceof AssignBoat)
							{
								watcherAID = aclMessage.getSender();
								myOntologyBoat = ((AssignBoat) concept).getBoat();
								myBoatAID = myOntologyBoat.getID();
								
								if (myOntologyBoat != null && myBoatAID != null)
								{
									printOut(" Assigned to boat "+myBoatAID.getLocalName());
									
									//Inform boat of the seat that the first rower picks
									if (currentPickSeat == null && (myAgent.getLocalName().compareTo(Constants.ROWER21NAME) == 0))
									{
										setCurrentPickSeat(5);
										
										sendCurrentPickSeat(myBoatAID);
									}
								}
								else
								{
									if (myOntologyBoat == null)
									{
										printOut("boatAID property (accessible via AssignBoat.getBoatID()) in AssignBoat instance received not set - note that AssignBoat.getBoatID() returns a upm.pb4agent.ontology.Boat instance (not an AID)");
									}
									else
									{
										printOut("iD property (accessible via AssignBoat.getBoatID().getID()) in upm.pb4agent.ontology.Boat instance received inside AssignBoat not set - note that AssignBoat.getBoatID() returns a upm.pb4agent.ontology.Boat instance (not an AID)");
									}
								}
							}
							
							/*else if (concept instanceof RealRow)
							{
								// Access RealRow instances sent from watcher here...
								
								RealRow realRow = ((RealRow) concept);
								
								printOut("Received a RealRow instance for oarID: "+realRow.getOarID()+" from " + aclMessage.getSender().getLocalName()+" with values x = "+realRow.getX()+" and y = "+realRow.getY());
							}*/
						}
					} 
					catch (CodecException e) 
					{
						e.printStackTrace();
					} 
					catch (OntologyException oe) 
					{
						oe.printStackTrace();
					}
				} 
				else if (aclMessage.getPerformative() == ACLMessage.AGREE) 
				{
					printOut("received AGREE");
				} 
				
				else if (aclMessage.getPerformative() == ACLMessage.REFUSE) 
				{
					try 
					{
						ContentElement contentElement = null;
						contentElement = getContentManager().extractContent(aclMessage);
						
						if (contentElement instanceof AgentAction)
						{
							Action action = (Action) contentElement;
							Concept concept = action.getAction();
							
							if (concept instanceof DenyRow)
							{
								// attempt to row again
								if(!raceFinished)
								{
									startRowing();
								}
								
								// Access DenyRow instances sent from watcher here...
								
								DenyRow denyRow = ((DenyRow) concept);
								printOut("Received a DenyRow instance from attempt to row on oarID: "+denyRow.getOarID()+" from " + aclMessage.getSender().getLocalName());
							}
							
							else if (concept instanceof PickSeat)
							{
								//PickSeat was denied - resend the PickSeat attempt
								sendCurrentPickSeat(myBoatAID);
								
								printOut("PickSeat request was denied for seatID = "+((PickSeat)concept).getSeatID().getSeatID());
							}
						}
					}
					catch (CodecException e) 
					{
						e.printStackTrace();
					} 
					catch (OntologyException oe) 
					{
						oe.printStackTrace();
					}
				}
				else if (aclMessage.getPerformative() == ACLMessage.NOT_UNDERSTOOD) 
				{
					// Message format is wrong
					printOut("received NOT_UNDERSTOOD");
				}
			} 
			else 
			{
				// No message yet
				block();
			}
		}

		@Override
		public boolean done() 
		{
			return false;
		}

	}

}
